Today in the workflow

Specify → Identify → Estimate → Evaluate → Revise/Report

Today we focus on:

  • Evaluate: “Does the same measurement model hold across groups?”
  • Report: “What level of invariance supports which claims?”

In particular:

  • Why measurement invariance is a prerequisite for group comparisons
  • The MG-CFA ladder (configural → metric → scalar → strict)
  • How to evaluate invariance (Δ fit + local diagnostics)
  • Partial invariance: what to free, how to justify it
  • (Briefly) structural invariance: variances/covariances/means

Learning objectives

By the end of this lesson you can:

  • Explain configural / metric / scalar / strict invariance in plain language
  • Fit MG-CFA models in lavaan using group.equal
  • Compare nested models using ΔCFI / ΔRMSEA / Δχ² (and know the limits)
  • Use score tests / modification indices to diagnose non-invariance
  • Implement partial invariance via group.partial
  • State which inferences are valid under each invariance level

A hot topic

Introduction

The importance of Measurement Invariance

  • Researchers often compare groups on psychological constructs, assuming instruments measure the same latent variables across groups.
  • This assumption is often untested.
  • Measurement invariance is a prerequisite for meaningful comparisons across groups (and across time).

Invariance of a Structural Equation Model

More generally, testing invariance evaluates to what extent a hypothesized SEM can be considered invariant (same parameters) across groups.

Invariance is commonly used for comparisons across:

  • gender
  • age groups
  • clinical vs non-clinical
  • culture / language / nationality
  • time (longitudinal invariance)

Assessing invariance: the multi-group analysis

  • Multi-group analysis is the most widely used method to assess invariance.
  • In this lesson we focus on CFA models (MG-CFA).
  • Logic: estimate the same model in multiple groups, then add equality constraints and check what breaks.

The starting point (MG-CFA)

The idea (constraints ladder)

  1. Start with the same model in all groups (baseline: all structural parameters are free to vary across groups)
  2. Add constraints (more restrictive models: loadings, intercepts, residual variances, … fixed to be equal)
  3. Compare fit: does the constraint produce a meaningful worsening?

Invariance steps (measurement)

  1. Configural: same factor structure (same “form”)
  2. Metric / weak: equal loadings (Λ)
  3. Scalar / strong: equal intercepts (τ)
  4. Strict: equal residual variances (Θ)

Scalar invariance (full or partial) is the gateway to latent mean comparisons.

What level do you need for which claim?

Claim / Comparison Minimum invariance
Same factor structure (same “form”) Configural
Compare relations (regressions/correlations) Metric (often)
Compare latent means Scalar (often partial scalar)
Compare observed means directly Not recommended without invariance evidence

Step-by-step guide (summary)

Step Model Constrain Compare to
0 Separate CFAs
1 Configural
2 Metric loadings configural
3 Scalar loadings + intercepts metric
4 Strict + residual variances scalar
5–7 Structural invariance lv variances/covariances/means previous

MG-CFA scheme (steps 1–4)

Slide stolen from Psicostat’s meeting by Enrico Perinelli.

TO READ

All the next slides are inspired in large part from the following blog post

Step 0: Separate models for each group

Step 1: Configural invariance

Step 1: Configural (non-)invariance

Configural invariance means that the “form” of the models is the same in the groups of interest. Form entails both the number of latent variables and whether the loadings are non-zero to begin with.

Step 2: Metric invariance

Step 2: Metric (non-)invariance

Metric invariance means that for each item, the loading of the factor on the item is the same in the two groups (or, again more precisely, that we cannot reject the hypothesis that the loadings are the same).

Step 2: Metric (non-)invariance

The source of group differences does not come from the latent variable!

Step 3: Scalar invariance

Step 3: Scalar invariance

Scalar invariance means that for each item, the intercept is the same. This means that group differences in the item responses are fully accounted for by group differences in the latent construct.

Step 4: Invariance of observed residual variances

Residual invariance means that for each item, the residual variance—the variance of the ominous E pointing into the items—is the same. We can again phrase this statistically: if we regressed the item scores on the factor, then the variance of the remaining residual would be the same in the groups (i.e., there would be homoscedasticity).

Step 4: Invariance of observed residual variances

The thing about the residual is that it captures everything that’s not explained in the model, and explaining changes in the amount of unexplained things seems a bit futile. Residual invariance is often not tested because it’s not necessary for latent mean comparisons. It’s a bit of an anticlimactic level to end on.

Step 5: Invariance of latent variances

Step 6: Invariance of latent covariances

Step 7: Invariance of latent means

Evaluation: global comparison

Common ingredients:

  • Model fit in the more constrained model (χ², RMSEA, CFI/TLI, SRMR)
  • Change in fit vs less constrained model:
    • Δχ² (highly N-sensitive)
    • ΔCFI (often used)
    • ΔRMSEA / ΔSRMR (sometimes used)
    • ΔBIC (information criteria)

A marked worsening of fit indices indicates that the considered invariance model is too restrictive and thus must be rejected.

Warning

make a comprehensive evaluation based on different fit indices, rather than on a single fit criterion.

Practical comparison rules

Let MOD-A be a reference model and MOD-B a more restrictive model.

  • Δχ² = χ²(B) − χ²(A) (N-sensitive; use with caution)
  • ΔCFI = CFI(B) − CFI(A)
    • heuristic: accept if ΔCFI > −.01
  • ΔBIC = BIC(B) − BIC(A)
    • negative values favor B

Warning

Pitfall: with robust estimators (e.g., MLR/WLSMV), the χ² difference test is not always the plain anova() test.

Partial invariance (what it is)

When invariance is untenable at some level (metric/scalar/strict), you can allow some parameters to differ:

  • Free non-invariant loadings/intercepts/residual variances
  • Keep most constraints to preserve comparability

Options (in practice):

  1. Free a small number of parameters (theory-driven)
  2. Argue differences are negligible (rarely convincing)
  3. Remove problematic indicators (last resort)
  4. Conclude constructs are not comparable with the current instrument or theoretically (sometimes correct!)

How do we choose what to free?

  • Inspect parameters by group (largest differences)
  • Inspect score tests / modification indices for constrained parameters
  • Free one at a time and re-evaluate
  • Always justify based on theory and measurement logic

Warning

Pitfall: “MI shopping” can overfit noise. Partial invariance must remain interpretable.

Live coding — a small MG-CFA example

CFA model (toy)

Step 0 — Separate models (sanity check)

This is not configural invariance. It’s only: “Does the model work in each group?”

m0 <- cfa(m, data = d[d$Group == 0, ])
m1 <- cfa(m, data = d[d$Group == 1, ])

fitMeasures(m0, fi)
     cfi      tli     srmr    rmsea      bic      aic 
   1.000    1.021    0.034    0.000 5214.258 5162.428 
fitMeasures(m1, fi)
     cfi      tli     srmr    rmsea      bic      aic 
   0.945    0.910    0.040    0.050 7899.949 7841.867 

Step 1 — Configural invariance

m_all <- cfa(m, data = d)
fitMeasures(m_all, fi)
      cfi       tli      srmr     rmsea       bic       aic 
    1.000     1.007     0.021     0.000 13070.106 13004.430 
m_conf <- cfa(m, data = d, group = "Group")
fitMeasures(m_conf, fi)
      cfi       tli      srmr     rmsea       bic       aic 
    0.976     0.961     0.034     0.035 13224.946 13032.295 

Step 2 — Metric invariance (equal loadings)

m_metr <- cfa(m, data = d, group = "Group",
             group.equal = "loadings")

fitMeasures(m_metr, fi)
      cfi       tli      srmr     rmsea       bic       aic 
    0.976     0.967     0.038     0.032 13198.034 13027.276 
# Compare to configural
anova(m_metr, m_conf)

Chi-Squared Difference Test

       Df   AIC   BIC  Chisq Chisq diff RMSEA Df diff Pr(>Chisq)
m_conf 26 13032 13225 35.392                                    
m_metr 31 13027 13198 40.373     4.9804     0       5     0.4183
fitMeasures(m_metr, "cfi") - fitMeasures(m_conf, "cfi")
cfi 
  0 
fitMeasures(m_metr, "bic") - fitMeasures(m_conf, "bic")
    bic 
-26.912 

Step 2 diagnostics (what breaks?)

# Score tests for equality constraints
lavTestScore(m_metr)$uni |> head(10)

univariate score tests:

   lhs op   rhs    X2 df p.value
1 .p2. == .p28. 2.336  1   0.126
2 .p3. == .p29. 0.310  1   0.578
3 .p4. == .p30. 0.877  1   0.349
4 .p6. == .p32. 1.798  1   0.180
5 .p7. == .p33. 0.227  1   0.634
# Parameter table (useful for mapping constraint ids)
parameterTable(m_metr)[1:15, c("lhs","op","rhs","group","free","ustart")]
   lhs op rhs group free ustart
1   f1 =~  x1     1    0      1
2   f1 =~  x2     1    1     NA
3   f1 =~  x3     1    2     NA
4   f1 =~  x4     1    3     NA
5   f2 =~  x5     1    0      1
6   f2 =~  x6     1    4     NA
7   f2 =~  x7     1    5     NA
8   x1 ~~  x1     1    6     NA
9   x2 ~~  x2     1    7     NA
10  x3 ~~  x3     1    8     NA
11  x4 ~~  x4     1    9     NA
12  x5 ~~  x5     1   10     NA
13  x6 ~~  x6     1   11     NA
14  x7 ~~  x7     1   12     NA
15  f1 ~~  f1     1   13     NA

Step 3 — Scalar invariance (loadings + intercepts)

m_scal <- cfa(m, data = d, group = "Group",
             group.equal = c("loadings","intercepts"))

fitMeasures(m_scal, fi)
      cfi       tli      srmr     rmsea       bic       aic 
    0.984     0.981     0.039     0.024 13168.052 13019.185 
anova(m_scal, m_metr)

Chi-Squared Difference Test

       Df   AIC   BIC  Chisq Chisq diff RMSEA Df diff Pr(>Chisq)
m_metr 31 13027 13198 40.373                                    
m_scal 36 13019 13168 42.283     1.9096     0       5     0.8615
fitMeasures(m_scal, "cfi") - fitMeasures(m_metr, "cfi")
  cfi 
0.008 

What can we fix?

Through the option group.equal , it is possible to constrain groups of parameters to be equal across groups in order to assess increasingly restrictive invariance hypotheses:

Constrained parameters In R
Factor loadings loadings
Intercepts of manifest variables intercepts
Residual variances of manifest variables residuals
Residual covariances of manifest variables residual.covariances
Residual variances of latent variables lv.variances
Residual covariances of latent variable lv.covariances
Intercepts/means of latent variables means
All regression coefficients regressions
Thresholds thresholds

Partial invariance in lavaan

group.partial allows selected parameters to differ.

Example: After the inspection of MI, we decided to estimate a model of partial metric invariance in which the loading of the item x5 is free to vary between groups::

m_metr_p <- cfa(m, data = d, group = "Group",
               group.equal = "loadings",
               group.partial = "f2 =~ x5")

fitMeasures(m_metr_p, fi)
      cfi       tli      srmr     rmsea       bic       aic 
    0.976     0.967     0.038     0.032 13198.034 13027.276 

Which model should we compare this to, and why? How to we interpret the results of such a model?

Common mistakes (invariance)

  • Comparing latent means without (partial) scalar invariance
  • Freeing many parameters without theoretical rationale (“MI fishing”)
  • Ignoring identification choices (scaling, reference indicators, factor means)
  • Using automated helpers as a black box

Case study

The case study dataset

load("../data/dmg.RData")
str(dmg)
'data.frame':   300 obs. of  10 variables:
 $ id       : int  1 2 3 4 5 6 7 8 9 10 ...
 $ diagnosis: Factor w/ 2 levels "manic","norming": 1 1 1 1 1 1 1 1 1 1 ...
 $ Info     : num  8.95 11.94 5.8 14.69 6 ...
 $ Sim      : num  9.34 9.93 6.64 17.72 6.56 ...
 $ Vocab    : num  12.39 4.57 6.03 13.21 7.83 ...
 $ Comp     : num  11.61 8.86 5.03 13.38 5.77 ...
 $ PicComp  : num  11.15 4.95 8.02 11.54 8.84 ...
 $ PicArr   : num  15.7 5.46 7.65 12.06 5.39 ...
 $ BlkDsgn  : num  11.45 3.43 9.28 10.46 7.39 ...
 $ ObjAsmb  : num  15.54 3.8 8.63 14.38 9.92 ...

Important

SEM work with variance-covariance matrices. For this reason, it is sufficient to find it in the original articles to use their “data”! The data we will use have been generated based on the parameters provided in the article, modifying the sample size.

Model

model <- "
gc =~ Info + Sim + Vocab + Comp
gv =~ PicComp + PicArr + BlkDsgn + ObjAsmb
"

Step 0 — Separate models

m_man <- cfa(model, data = dmg[dmg$diagnosis == "manic", ])
m_nor <- cfa(model, data = dmg[dmg$diagnosis == "norming", ])

fitMeasures(m_man, c("chisq","df","rmsea","cfi","tli"))
 chisq     df  rmsea    cfi    tli 
54.052 19.000  0.111  0.949  0.924 
fitMeasures(m_nor, c("chisq","df","rmsea","cfi","tli"))
 chisq     df  rmsea    cfi    tli 
18.151 19.000  0.000  1.000  1.003 

Step 1 — Configural

m_conf2 <- cfa(model, data = dmg, group = "diagnosis")
fitMeasures(m_conf2, c("chisq","df","rmsea","cfi","tli"))
 chisq     df  rmsea    cfi    tli 
72.203 38.000  0.077  0.971  0.957 

ANY COMMENTS?

Step 2 — Metric

m_metr2 <- cfa(model, data = dmg, group = "diagnosis",
              group.equal = "loadings")

fitMeasures(m_metr2, c("chisq","df","rmsea","cfi","tli"))
 chisq     df  rmsea    cfi    tli 
88.153 44.000  0.082  0.963  0.952 

Step 2 — Metric

anova(m_metr2, m_conf2)

Chi-Squared Difference Test

        Df   AIC   BIC  Chisq Chisq diff   RMSEA Df diff Pr(>Chisq)  
m_conf2 38 11239 11424 72.203                                        
m_metr2 44 11243 11406 88.153      15.95 0.10515       6    0.01402 *
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
fitMeasures(m_metr2, "cfi") - fitMeasures(m_conf2, "cfi")
   cfi 
-0.008 
fitMeasures(m_metr2, "bic") - fitMeasures(m_conf2, "bic")
    bic 
-18.272 

Step 3 — Scalar and partial scalar

m_scal2 <- cfa(model, data = dmg, group = "diagnosis",
              group.equal = c("loadings","intercepts"))

fitMeasures(m_scal2, c("chisq","df","rmsea","cfi","tli"))
  chisq      df   rmsea     cfi     tli 
144.667  50.000   0.112   0.920   0.910 

COMMENTS

Step 3 — Scalar and partial scalar

anova(m_scal2, m_metr2)

Chi-Squared Difference Test

        Df   AIC   BIC   Chisq Chisq diff   RMSEA Df diff Pr(>Chisq)    
m_metr2 44 11243 11406  88.153                                          
m_scal2 50 11287 11428 144.667     56.513 0.23691       6  2.292e-10 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
fitMeasures(m_scal2, "cfi") - fitMeasures(m_metr2, "cfi")
   cfi 
-0.043 

Step 3 — Inspection of equality constraints

# Identify the worst constraints
lavTestScore(m_scal2)$uni |> head(10)

univariate score tests:

     lhs op   rhs     X2 df p.value
1   .p2. == .p31.  0.419  1   0.517
2   .p3. == .p32.  0.335  1   0.563
3   .p4. == .p33.  2.389  1   0.122
4   .p6. == .p35.  7.026  1   0.008
5   .p7. == .p36.  0.072  1   0.789
6   .p8. == .p37.  0.000  1   0.988
7  .p20. == .p49.  8.342  1   0.004
8  .p21. == .p50. 42.173  1   0.000
9  .p22. == .p51.  2.691  1   0.101
10 .p23. == .p52. 11.089  1   0.001
parameterTable(m_scal2)

Step 3 — Partial scalar invariance

# Example: free the intercept of Sim
m_scal2_p <- cfa(model, data = dmg, group = "diagnosis",
                group.equal = c("loadings","intercepts"),
                group.partial = "Sim ~ 1")
fitMeasures(m_scal2_p, c("chisq","df","rmsea","cfi","tli"))
  chisq      df   rmsea     cfi     tli 
100.174  49.000   0.083   0.957   0.950 

Step 3 — Partial scalar invariance

anova(m_scal2_p, m_metr2)

Chi-Squared Difference Test

          Df   AIC   BIC   Chisq Chisq diff    RMSEA Df diff Pr(>Chisq)  
m_metr2   44 11243 11406  88.153                                         
m_scal2_p 49 11245 11389 100.174     12.021 0.096752       5     0.0345 *
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
fitMeasures(m_scal2_p, "cfi") - fitMeasures(m_metr2, "cfi")
   cfi 
-0.006 

What does partial scalar mean (interpretation)?

  • Most intercepts are comparable across groups
  • A small subset is not (e.g., Sim)
  • Latent mean comparisons can be more defensible under partial scalar, but interpret non-invariant indicators substantively

Which item behaves differently across groups, and what could it reflect?

Invariance of Residuals of observed variables(1)

# Note: parameter "Sim~1" remains free
m.rvo=cfa(model,dmg,group="diagnosis",
          group.equal=c("loadings","intercepts","residuals"),
          group.partial="Sim~1") #Note that this is still here
# Inspection of fit indices
fitMeasures(m.rvo,c("chisq","df","rmsea","cfi","nnfi"))
  chisq      df   rmsea     cfi    nnfi 
127.344  57.000   0.091   0.940   0.941 

Invariance of Residuals of observed variables (2)

anova(m.rvo,m_scal2_p) # Note: Comparison model Partial Scalar invariance

Chi-Squared Difference Test

          Df   AIC   BIC  Chisq Chisq diff   RMSEA Df diff Pr(>Chisq)    
m_scal2_p 49 11245 11389 100.17                                          
m.rvo     57 11256 11371 127.34     27.169 0.12639       8  0.0006609 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
fitMeasures(m.rvo,"cfi")-fitMeasures(m_scal2_p,"cfi")
   cfi 
-0.016 
fitMeasures(m.rvo,"bic")-fitMeasures(m_scal2_p,"bic")
    bic 
-18.461 
# The Invariance of Residuals of observed variables
#   is not satisfactory. Let's take a look at equality constraints

Inspection of equality constraints

lavTestScore(m.rvo)$uni |> head(10)

univariate score tests:

     lhs op   rhs     X2 df p.value
1   .p2. == .p31.  4.802  1   0.028
2   .p3. == .p32.  1.156  1   0.282
3   .p4. == .p33.  0.138  1   0.710
4   .p6. == .p35.  7.948  1   0.005
5   .p7. == .p36.  0.099  1   0.753
6   .p8. == .p37.  0.020  1   0.888
7   .p9. == .p38.  0.107  1   0.744
8  .p10. == .p39.  0.108  1   0.743
9  .p11. == .p40.  0.504  1   0.478
10 .p12. == .p41. 12.169  1   0.000
# (... see complete output )
# From a first analysis, we can see that
#  the residuals of variables  “Comp” and “PicComp”
#  have Modification indices that are
#  particularly high, so let's free them

Invariance of Residuals of observed variables (1)

m.rvo.P=cfa(model,dmg,group="diagnosis",
            group.equal=c("loadings","intercepts","residuals"),
            group.partial=c("Sim~1","PicComp~~PicComp","Comp~~Comp"))
# Fit indices
fitMeasures(m.rvo.P,c("chisq","df","rmsea","cfi","nnfi"))
  chisq      df   rmsea     cfi    nnfi 
102.420  55.000   0.076   0.960   0.959 

Partial Invariance of Residuals of observed variables (2)

# Evaluation of Partial Invariance of Residuals of observed variables
anova(m.rvo.P,m_scal2_p) # Note: Comparison model Partial Scalar invariance

Chi-Squared Difference Test

          Df   AIC   BIC  Chisq Chisq diff RMSEA Df diff Pr(>Chisq)
m_scal2_p 49 11245 11389 100.17                                    
m.rvo.P   55 11235 11357 102.42     2.2458     0       6     0.8958
fitMeasures(m.rvo.P,"cfi")-fitMeasures(m_scal2_p,"cfi")
  cfi 
0.003 
fitMeasures(m.rvo.P,"bic")-fitMeasures(m_scal2_p,"bic")
    bic 
-31.977 
# Paartial Invariance of Residuals of observed variables is satisfactory.
# Question: What can we say about overall
# Measurement Invariance of the baseline theoretical model?

Invariance of Variance of latent variables (1)

m.vvl=cfa(model,dmg,group="diagnosis",
          group.equal=c("loadings","intercepts","residuals",
                        "lv.variances"),
          group.partial=c("Sim~1","PicComp~~PicComp","Comp~~Comp"))
# Fit indices
fitMeasures(m.vvl,c("chisq","df","rmsea","cfi","nnfi"))
  chisq      df   rmsea     cfi    nnfi 
105.825  57.000   0.076   0.959   0.959 

Invariance of Variance of latent variables (2)

anova(m.vvl,m.rvo.P)

Chi-Squared Difference Test

        Df   AIC   BIC  Chisq Chisq diff    RMSEA Df diff Pr(>Chisq)
m.rvo.P 55 11235 11357 102.42                                       
m.vvl   57 11234 11349 105.83     3.4056 0.068449       2     0.1822
fitMeasures(m.vvl,"cfi")-fitMeasures(m.rvo.P,"cfi")
   cfi 
-0.001 
fitMeasures(m.vvl,"bic")-fitMeasures(m.rvo.P,"bic")
   bic 
-8.002 
# OK, this looks good!

Invariance of Covariance of latent variables (1)

m.cvl=cfa(model,dmg,group="diagnosis",
          group.equal=c("loadings","intercepts"
                        ,"residuals","lv.variances","lv.covariances"),
          group.partial=c("Sim~1","PicComp~~PicComp","Comp~~Comp"))
# Fit indices
fitMeasures(m.cvl,c("chisq","df","rmsea","cfi","nnfi"))
  chisq      df   rmsea     cfi    nnfi 
106.412  58.000   0.075   0.959   0.960 

Invariance of Covariance of latent variables(2)

anova(m.cvl,m.vvl)

Chi-Squared Difference Test

      Df   AIC   BIC  Chisq Chisq diff RMSEA Df diff Pr(>Chisq)
m.vvl 57 11234 11349 105.83                                    
m.cvl 58 11233 11344 106.41    0.58642     0       1     0.4438
fitMeasures(m.cvl,"cfi")-fitMeasures(m.vvl,"cfi")
cfi 
  0 
fitMeasures(m.cvl,"bic")-fitMeasures(m.vvl,"bic")
   bic 
-5.117 
# OK, we're almost there!

Invariance of Means of latent variables (1)

# last step
m.med=cfa(model,dmg,group="diagnosis",
          group.equal=c("loadings","intercepts"
                        ,"residuals","lv.variances","lv.covariances",
                        "means"),
          group.partial=c("Sim~1","PicComp~~PicComp","Comp~~Comp"))
# Fit indices
fitMeasures(m.med,c("chisq","df","rmsea","cfi","nnfi"))
  chisq      df   rmsea     cfi    nnfi 
110.933  60.000   0.075   0.957   0.960 

Invariance of Means of latent variables (2)

anova(m.med,m.cvl)

Chi-Squared Difference Test

      Df   AIC   BIC  Chisq Chisq diff    RMSEA Df diff Pr(>Chisq)
m.cvl 58 11233 11344 106.41                                       
m.med 60 11234 11337 110.93     4.5212 0.091673       2     0.1043
fitMeasures(m.med,"cfi")-fitMeasures(m.cvl,"cfi")
   cfi 
-0.002 
fitMeasures(m.med,"bic")-fitMeasures(m.cvl,"bic")
   bic 
-6.886 
# This last model is also satisfactory
# ANY COMMENTS?

Summing up

Models npar df chisq cfi tli nnfi agfi srmr rmsea bic aic
Manics 17 19 54.05 0.95 0.92 0.92 0.84 0.05 0.11 5590.65 5539.47
Norming 17 19 18.15 1 1 1 0.94 0.03 0 5718.64 5667.46
Configural 50 38 72.2 0.97 0.96 0.96 0.98 0.04 0.08 11424.11 11238.93
Metric 44 44 88.15 0.96 0.95 0.95 0.98 0.06 0.08 11405.84 11242.88
Scalar 38 50 144.67 0.92 0.91 0.91 0.97 0.08 0.11 11428.13 11287.39
Scalar Partial 39 49 100.17 0.96 0.95 0.95 0.98 0.07 0.08 11389.34 11244.9
Residual Variances 31 57 127.34 0.94 0.94 0.94 0.98 0.08 0.09 11370.88 11256.07
Residual Variances Partial 33 55 102.42 0.96 0.96 0.96 0.98 0.07 0.08 11357.37 11235.14
Latent Variances 31 57 105.83 0.96 0.96 0.96 0.98 0.09 0.08 11349.36 11234.55
Latent Covariances 30 58 106.41 0.96 0.96 0.96 0.98 0.09 0.07 11344.25 11233.13
Latent Means 28 60 110.93 0.96 0.96 0.96 0.98 0.09 0.08 11337.36 11233.66

Structural invariance

Test of multigroup invariance can also be used to compare differences in the regression coefficients of two or more groups (interactions).

Exercise (connect to the lab)

Important

Try it (15–25 min)
1. Fit configural, metric, scalar on the toy dataset d.
2. Decide whether scalar is acceptable using ΔCFI + theory.
3. If scalar fails, free one intercept using group.partial.
4. Write 3–4 sentences: what is comparable now, and what is not?

Lab link placeholder: add a direct link to the invariance lab once created (e.g., ../labs/lab04_invariance.qmd).

Take-home: 3 things

  1. Invariance is about comparability
  2. Scalar (full/partial) is the key for latent mean comparisons.
  3. Partial invariance is legitimate, but must be disciplined + theory-driven.

Further reading (extras)

  • Partial invariance strategies and interpretability
  • Invariance with ordinal indicators (thresholds first)
  • Robust difference testing with MLR / WLSMV

Add links to extras/ modules when we create them.

References